﻿namespace JoinBox.Basal;

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;


public partial class WindowsAPI
{
    /// <summary>
    /// <see cref="GetCursorPos"/> 获得当前鼠标指针位置<br/>
    /// <see cref="WindowFromPoint"/> 获得当前鼠标所指的窗口句柄<br/>
    /// <see cref="GetWindowThreadProcessId"/> 获得该窗口所属的进程id<br/>
    /// <see cref="CreateToolhelp32Snapshot"/> 获得系统所有进程的快照<br/>
    /// <see cref="Process32First"/> 和 <see cref="Process32Next"/> 获得各个进程的详细信息<br/>
    /// 比对详细信息中的进程id与前面所得到的进程id<br/>
    /// 进程id匹配的进程信息结构 <see cref="ProcessEntry32"/> 里面就包含该进程的所有信息了<br/>
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct ProcessEntry32
    {
        /// <summary>
        /// 结构大小
        /// </summary>
        public uint dwSize;
        /// <summary>
        /// 此进程的引用计数
        /// </summary>
        public uint cntUsage;
        /// <summary>
        /// 进程ID
        /// </summary>
        public uint th32ProcessID;
        /// <summary>
        /// 进程默认堆ID
        /// </summary>
        public IntPtr th32DefaultHeapID;
        /// <summary>
        /// 进程模块ID
        /// </summary>
        public uint th32ModuleID;
        /// <summary>
        /// 此进程开启的线程计数
        /// </summary>
        public uint cntThreads;
        /// <summary>
        /// 父进程ID
        /// </summary>
        public uint th32ParentProcessID;
        /// <summary>
        /// 线程优先权
        /// </summary>
        public int pcPriClassBase;
        /// <summary>
        /// 保留
        /// </summary>
        public uint dwFlags;
        /// <summary>
        /// 进程全名
        /// </summary>
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szExeFile;
    };


    /// <summary>
    /// 获得系统所有进程的快照
    /// </summary>
    /// <param name="flags">用来指定“快照”中需要返回的对象</param>
    /// <param name="processid">进程ID号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取,当前进程快照时可以设为0</param>
    /// <returns></returns>
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr CreateToolhelp32Snapshot(TH32CS flags, uint processid);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern int CloseHandle(IntPtr handle);

    /// <summary>
    /// 获得各个进程的详细信息
    /// </summary>
    /// <param name="handle"></param>
    /// <param name="pe"></param>
    /// <returns></returns>
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern int Process32First(IntPtr handle, ref ProcessEntry32 pe);

    /// <summary>
    /// 获得各个进程的详细信息
    /// </summary>
    /// <param name="handle"></param>
    /// <param name="pe"></param>
    /// <returns></returns>
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern int Process32Next(IntPtr handle, ref ProcessEntry32 pe);



    // QueryFullProcessImageName 这个也是会获取名称的时候出错的
    // https://www.cnblogs.com/yuwei/archive/2011/04/26/2029434.html
    // https://zhidao.baidu.com/question/616395452144443772.html
    /// <summary>
    /// 当前进程获取进程名称win7以上
    /// </summary>
    /// <param name="processHandle">进程句柄</param>
    /// <param name="lpszFileName"></param>
    /// <param name="nSize"></param>
    /// <returns></returns>
    [DllImport("Kernel32.dll", EntryPoint = "QueryFullProcessImageNameW", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
    public static extern int QueryFullProcessImageName(IntPtr hProcess, uint flags, char[] exeName, ref uint nameLen);


    // https://www.baidu.com/link?url=mVKqIFLB6CUFGB1adxX5hf6o8xSJfpsc5OVpqFDqNM55XYA2KFbTL3NP2dZ59xhh7KhgSJFELlnkJABMF08qqxp6lBy9nSrmTQt9olErcX-X-SdfhH14dZc_p15MZ2eZCOf8WHyP-UDdR6KxC59bcLWH-dsWdxn-JMvdPQ3oIcW&wd=&eqid=d71a3f820002e3cd000000065f84636e
    // 用了这个代替虽然可以,但是频繁调用会出错
    // var process = Process.GetProcessById((int) pid);
    // var fileName = process.MainModule.FileName;

    /// <summary>
    /// 跨进程获取进程名称
    /// </summary>
    /// <param name="hProcess"></param>
    /// <param name="hModule"></param>
    /// <param name="lpszFileName"></param>
    /// <param name="nSize"></param>
    /// <returns></returns>
    [DllImport("psapi.dll", CharSet = CharSet.Auto)]
    public static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpszFileName, int nSize);

#if false // 这些东西都有线程问题
    /// <summary>
    /// 当前进程获取进程名称 不好用
    /// </summary>
    /// <param name="processHandle">进程句柄</param>
    /// <param name="lpszFileName"></param>
    /// <param name="nSize"></param>
    /// <returns></returns>
    [DllImport("kernel32.dll")]
    public static extern uint GetModuleFileName(IntPtr processHandle, [Out] StringBuilder lpszFileName, int nSize);
#endif
}

public partial class WindowsAPI
{
    /// <summary>
    /// 点
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int X;
        public int Y;
        public POINT(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }
        public double Leng(POINT a)
        {
            return Math.Sqrt((X - a.X) * (X - a.X) + (Y - a.Y) * (Y - a.Y));
        }

        public override string ToString()
        {
            var str = new StringBuilder();
            str.Append(X);
            str.Append(",");
            str.Append(Y);
            return str.ToString();
        }
    }

    public enum ProcessEnum
    {
        // PROCESS_QUERY_INFORMATION  查询信息
        PROCESS_ALL_ACCESS = 0x1F0FFF, // 访问权限
        PROCESS_VM_READ = 0x0010,      // 读权限
        PROCESS_VM_WRITE = 0x0020,      // 写权限
    }

    /// <summary>
    /// 访问已经开启的进程
    /// </summary>
    /// <param name="dwDesiredAccess">访问权限</param>
    /// <param name="bInheritHandle">继承标志</param>
    /// <param name="dwProcessId">进程ID</param>
    /// <returns></returns>
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr OpenProcess(ProcessEnum dwDesiredAccess, bool bInheritHandle, IntPtr pid);

    /// <summary>
    /// 根据窗体找进程
    /// </summary>
    /// <param name="hwnd">窗体句柄</param>
    /// <param name="pid">如果参数不为NULL,拷贝到存放处</param>
    /// <returns>返回线程的id号</returns>
    [DllImport("user32")]
    public static extern uint GetWindowThreadProcessId(IntPtr hwnd, out uint pid);

    /// <summary>
    /// 根据进程找主窗口
    /// </summary>
    /// <param name="pid"></param>
    /// <returns></returns>
    [DllImport("user32")]
    public static extern int FindMainWindow(IntPtr pid);

    /// <summary>
    /// 指定坐标处窗体句柄
    /// </summary>
    /// <returns></returns>
    [DllImport("user32.dll")]
    public static extern IntPtr WindowFromPoint(POINT lpPoint);

    /// <summary>
    /// 获取鼠标焦点
    /// </summary>
    /// <param name="lpPoint"></param>
    /// <returns></returns>
    [DllImport("user32.dll")]
    public static extern bool GetCursorPos(out POINT lpPoint);

    /// <summary>
    /// 设置鼠标焦点
    /// </summary>
    [DllImport("user32.dll")]
    public static extern bool SetCursorPos(POINT pt);

    /// <summary>
    /// 备份鼠标点,然后还原鼠标点
    /// </summary>
    /// <param name="action"></param>
    public static void CursorPosBak(Action action)
    {
        if (!GetCursorPos(out POINT ptBak))
            return;
        action?.Invoke();
        SetCursorPos(ptBak);
    }

    /// <summary>
    /// 模拟鼠标移动和点击
    /// <a href="https://blog.csdn.net/biyusr/article/details/108376195">相关链接</a>
    /// </summary>
    /// <param name="dwFlags"></param>
    /// <param name="dx"></param>
    /// <param name="dy"></param>
    /// <param name="dwData"></param>
    /// <param name="dwExtraInfo"></param>
    /// <returns></returns>
    [DllImport("user32", EntryPoint = "mouse_event")]
    public static extern int mouse_event(MOUSEEVENTF dwFlags, int dx, int dy, int dwData, int dwExtraInfo);

    /// <summary>
    /// 模拟鼠标原地双击
    /// </summary>
    public static void DoubleClick()
    {
        // 如果没有使用 MOUSEEVENTF.ABSOLUTE,函数默认的是相对于鼠标当前位置的点,
        // 如果dx,和dy,用0,0表示,这函数认为是当前鼠标所在的点
        mouse_event(MOUSEEVENTF.LEFTDOWN | MOUSEEVENTF.LEFTUP, 0, 0, 0, 0);
        mouse_event(MOUSEEVENTF.LEFTDOWN | MOUSEEVENTF.LEFTUP, 0, 0, 0, 0);
    }


    /// <summary>
    /// 获取当前线程ID
    /// </summary>
    /// <returns></returns>
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern uint GetCurrentThreadId();

    /// <summary>
    /// 加载库
    /// </summary>
    /// <param name="lpFileName"></param>
    /// <returns></returns>
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr LoadLibrary(string lpFileName);

    // https://blog.csdn.net/shaoyiju/article/details/83796293?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-2.baidujs&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-2.baidujs
    // 若DLL不在调用方的同一目录下,可以用LoadLibrary(L"DLL绝对路径")加载
    // 但若调用的DLL内部又调用另外一个DLL,此时调用仍会失败
    // 解决办法是用 LoadLibraryEx,通过指定 LOAD_WITH_ALTERED_SEARCH_PATH,让系统DLL搜索顺序从DLL所在目录开始。
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, EntryPoint = "LoadLibraryEx")]
    public static extern IntPtr LoadLibraryEx2(string lpFileName, long hFile, long dwFlags);

    public static IntPtr LoadLibraryEx(string lpFileName, long hFile = 0, long dwFlags = 0x00000008)
    {
        // var LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
        return LoadLibraryEx2(lpFileName, hFile, dwFlags);
    }

    /// <summary>
    /// 释放库
    /// </summary>
    /// <param name="loadLibraryIntPtr">句柄</param>
    /// <returns></returns>
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr FreeLibrary(IntPtr loadLibraryIntPtr);


    /// <summary>
    /// 获取一个应用程序或dll的模块句柄,要求已经载入
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr GetModuleHandle(string name);


    /// <summary>
    /// 获取要引入的函数,将符号名或标识号转换为DLL内部地址
    /// </summary>
    /// <param name="hModule">exe/dll句柄</param>
    /// <param name="procName">接口名</param>
    /// <returns></returns>
    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true)]
    public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

    /// <summary>
    /// 读取过程存储器
    /// </summary>
    /// <param name="hProcess"></param>
    /// <param name="lpBaseAddress"></param>
    /// <param name="lpBuffer"></param>
    /// <param name="dwSize"></param>
    /// <param name="lpNumberOfBytesRead"></param>
    /// <returns></returns>
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out()] byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);

    /// <summary>
    /// 虚拟保护
    /// </summary>
    /// <param name="lpAddress"></param>
    /// <param name="dwSize"></param>
    /// <param name="flNewProtect"></param>
    /// <param name="lpflOldProtect"></param>
    /// <returns></returns>
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flNewProtect, ref uint lpflOldProtect);


    // https://blog.csdn.net/u013075699/article/details/45749661

    /// <summary>
    /// 设置钩子
    /// </summary>
    /// <param name="idHook">钩子类型,此处用整形的枚举表示</param>
    /// <param name="lpfn">钩子发挥作用时的回调函数</param>
    /// <param name="hInstance">应用程序实例的模块句柄(一般来说是你钩子回调函数所在的应用程序实例模块句柄)</param>
    /// <param name="threadId">安装的钩子子程相关联的线程的标识符</param>
    /// <returns></returns>
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern int SetWindowsHookEx(
    HookType idHook,
    HookProc lpfn,
    IntPtr hInstance,
    uint threadId
    );

    // 卸载钩子
    [DllImport("user32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    public static extern bool UnhookWindowsHookEx(int idHook);
    // 调用下一个钩子
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);


    /// <summary>
    /// 钩子的回调函数
    /// </summary>
    /// <param name="nCode">钩子代码传递给当前的钩子过程。下一个钩子过程使用此代码来确定如何处理挂钩信息</param>
    /// <param name="wParam">所述的wParam传递给当前挂钩过程值。此参数的含义取决于与当前钩链相关联的钩子类型</param>
    /// <param name="lParam">所述的lParam传递给当前挂钩过程值。此参数的含义取决于与当前钩链相关联的钩子类型</param>
    /// <returns>0或CallNextHookEx()是继续传递到后面的钩子,1是不再传递</returns>
    public delegate int HookProc(int nCode, int wParam, IntPtr lParam);


    /// <summary>
    /// 转换指定的虚拟键码和键盘状态的相应字符或字符
    /// </summary>
    /// <param name="uVirtKey">指定虚拟关键代码进行翻译</param>
    /// <param name="uScanCode">指定的硬件扫描码的关键须翻译成英文。高阶位的这个值设定的关键,如果是（不压）</param>
    /// <param name="lpbKeyState">指针,以256字节数组,包含当前键盘的状态。每个元素（字节）的数组包含状态的一个关键。如果高阶位的字节是一套,关键是下跌（按下）。在低比特,如果设置表明,关键是对切换。在此功能,只有肘位的CAPS LOCK键是相关的。在切换状态的NUM个锁和滚动锁定键被忽略</param>
    /// <param name="lpwTransKey">[out] 指针的缓冲区收到翻译字符或字符</param>
    /// <param name="fuState">指定菜单是否处于活动状态。如果菜单处于活动状态,则此参数必须为1,否则为0</param>
    /// <returns></returns>
    [DllImport("user32")]
    public static extern int ToAscii(int uVirtKey, int uScanCode, byte[] lpbKeyState, byte[] lpwTransKey, int fuState);

    // 获取按键的状态
    [DllImport("user32")]
    public static extern int GetKeyboardState(byte[] pbKeyState);


    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    static extern short GetKeyState(int vKey);


    /// <summary>
    /// Hook键盘数据结构
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]// 默认就是这个排序的
    public class KeyMSG
    {
        public int vkCode;      // 键码,该代码必须有一个价值的范围1至254
        public int scanCode;    // 指定的硬件扫描码的关键
        public int flags;       // 键标志
        public int time;        // 指定的时间戳记的这个讯息
        public int dwExtraInfo; // 指定额外信息相关的信息
    }


#if true
    /// <summary>
    /// Hook鼠标数据结构
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public class MouseMsg
    {
        /// <summary>
        /// 鼠标点击
        /// </summary>
        public POINT pt;
        /// <summary>
        /// 点击窗体的句柄
        /// </summary>
        public int hWnd;
        /// <summary>
        ///
        /// </summary>
        public int wHitTestCode;
        /// <summary>
        /// 扩展信息,可以使用API函数GetMessageExtraInfo的返回值。例如单击鼠标左键的程序如下：
        /// </summary>
        public int dwExtraInfo;
    }
#endif
}

/// <summary>
/// 声明鼠标钩子的封送结构类型
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public class MOUSEHOOKSTRUCT
{
    /// <summary>
    /// POINT结构对象,保存鼠标在屏幕上的x,y坐标
    /// </summary>
    public Point pt;
    /// <summary>
    /// 接收到鼠标消息的窗口的句柄
    /// </summary>
    public IntPtr hWnd;
    /// <summary>
    /// hit-test值,详细描述参见WM_NCHITTEST消息
    /// </summary>
    public int wHitTestCode;
    /// <summary>
    /// 指定与本消息联系的额外消息
    /// </summary>
    public int dwExtraInfo;
}

/// <summary>
/// 键盘Hook结构函数
/// 即钩子发挥作用时能够得到的一些参数
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public class KBDLLHOOKSTRUCT
{
    /// <summary>
    /// 虚拟按键码(1--254)
    /// </summary>
    public int vkCode;
    /// <summary>
    /// 硬件按键扫描码
    /// </summary>
    public int scanCode;
    /// <summary>
    /// 键按下：128 抬起：0
    /// </summary>
    public int flags;
    /// <summary>
    /// 消息时间戳间
    /// </summary>
    public int time;
    /// <summary>
    /// 额外信息
    /// </summary>
    public int dwExtraInfo;
}

public partial class WindowsAPI
{
    [DllImport("user32", EntryPoint = "GetMessage")]
    public static extern int GetMessage(
            out TagMSG lpMsg,
            IntPtr hwnd,
            int wMsgFilterMin,
            int wMsgFilterMax
    );

    [DllImport("user32", EntryPoint = "DispatchMessage")]
    public static extern int DispatchMessage(
                  ref TagMSG lpMsg
    );

    [DllImport("user32", EntryPoint = "TranslateMessage")]
    public static extern int TranslateMessage(
                  ref TagMSG lpMsg
    );

    public struct TagMSG
    {
        public int hwnd;
        public uint message;
        public int wParam;
        public long lParam;
        public uint time;
        public int pt;
    }

    /// <summary>
    /// 判断函数调用时指定虚拟键的状态,确定用户当前是否按下了键盘上的一个键的函数。如果按下,则返回值。
    /// </summary>
    /// <param name="nVirtKey"></param>
    /// <returns></returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern short GetAsyncKeyState(int nVirtKey);

    /// <summary>
    /// 判断 alt\shift\control 是否有按键被按下
    /// </summary>
    /// <returns></returns>
    public static bool CONTROL_ALT_ShiftIsDown()
    {
        return GetAsyncKeyState((int)VK.VK_SHIFT) < 0 || GetAsyncKeyState((int)VK.VK_ALT) < 0 || GetAsyncKeyState((int)VK.VK_CONTROL) < 0;
    }
}